package cn.anlaser.service.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import cn.anlaser.entity.BasicEntity;
import cn.anlaser.form.CommonForm;
import cn.anlaser.page.QueryPage;
import cn.anlaser.page.QuerySpecification;
import cn.anlaser.repository.BasicRepository;
import cn.anlaser.service.BasicService;
import cn.anlaser.utils.BeanMapper;
import cn.anlaser.utils.DynamicSpecifications;
import cn.anlaser.utils.SearchFilter;


@Transactional(readOnly = true)
public abstract class BasicServiceImpl<Form extends CommonForm, Repository extends BasicRepository<Entity>, Entity extends BasicEntity>
		implements BasicService<Form, Repository, Entity> {
	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * @return
	 */
	public abstract Class<Entity> getEntityClazz();

	// @Cacheable(value = "idCache", key = "#id")
	@Override
	public Entity findById(Long id) {
		return getRepository().findOne(id);
	}

	@Transactional
	@Override
	public Entity add(Form form) {
		Entity obj = BeanMapper.map(form, getEntityClazz());
		traversalAssign(form, obj);
		return add(obj);
	}

	protected void traversalAssign(Serializable entity, Entity obj) {
		try {
			List<Field> srcFields = new ArrayList<Field>();
			getAllFields(srcFields, entity.getClass());

			Map<String, Field> distFields = new HashMap<String, Field>();
			getAllFields(distFields, obj.getClass());
			for (Field srcField : srcFields) {

				String mod = Modifier.toString(srcField.getModifiers());
				logger.info(mod + ":" + srcField.getName());
				if (srcField.getModifiers() == 26) {
					continue;
				}
				srcField.setAccessible(true);
				Object val = srcField.get(entity);
				if (val == null) {
					continue;
				}
				Field distField = distFields.get(srcField.getName());
				if (distField != null) {
					distField.setAccessible(true);
					distField.set(obj, val);
				}
			}
		} catch (IllegalArgumentException e) {
			
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			
			e.printStackTrace();
		} catch (SecurityException e) {
			
			e.printStackTrace();
		}
	}

	private void getAllFields(List<Field> resLst, Class<?> clazz) {
		Field[] fields = clazz.getDeclaredFields();
		if (resLst == null) {
			resLst = new ArrayList<Field>();
		}
		for (Field field : fields) {
			resLst.add(field);
		}
		if (!clazz.getSuperclass().getName().equals(Object.class.getName())) {
			getAllFields(resLst, clazz.getSuperclass());
		}
	}

	private void getAllFields(Map<String, Field> resMap, Class<?> clazz) {
		Field[] fields = clazz.getDeclaredFields();
		if (resMap == null) {
			resMap = new HashMap<String, Field>();
		}
		for (Field field : fields) {
			resMap.put(field.getName(), field);
		}
		if (!clazz.getSuperclass().getName().equals(Object.class.getName())) {
			getAllFields(resMap, clazz.getSuperclass());
		}
	}

	@Transactional
	@Override
	public Entity edit(Form form) {
		Entity obj = getRepository().findOne(form.getId());
		traversalAssign(form, obj);
		return edit(obj);
	}

	@Transactional
	@Override
	public Entity add(Entity entity) {
		return getRepository().save(entity);
	}

	@Transactional
	@Override
	public Entity edit(Entity entity) {
		if (entity.getId() == null || entity.getId() == 0) {
			// 错误，抛出不能修改
			logger.error("修改数据时，未传入ID");
		}
		return getRepository().saveAndFlush(entity);
	}

	@Override
	public QueryPage<Entity> findByPage(Pageable pageable) {
		/*
		 * return getRepository().findAll(new Specification<Entity>() {
		 * 
		 * @Override public Predicate toPredicate(Root<Entity> root,
		 * CriteriaQuery<?> query, CriteriaBuilder cb) { 
		 * method stub return null; } });
		 */
		QueryPage<Entity> resPage = new QueryPage<Entity>();
		resPage.setQueryLine("&size=" + pageable.getPageSize()
				+ "&orders=id&direction=" + Direction.DESC);
		resPage.setPage(getRepository().findAll(
				new PageRequest(pageable.getPageNumber(), pageable
						.getPageSize(), new Sort(Direction.DESC,
						new String[] { "id" }))));
		return resPage;
	}

	@Override
	public QueryPage<Entity> findAll(Form form, Pageable pageable) {
		QuerySpecification<Entity> spec = DynamicSpecifications.bySearchFilter(
				buildByForm(form).values(), getEntityClazz());
		Sort sort = new Sort(Direction.DESC, new String[] { "id" });
		// pageable.getSort()
		return findAll(
				spec,
				new PageRequest(pageable.getPageNumber(), pageable
						.getPageSize(), new Sort(Direction.DESC,
						new String[] { "id" })));
	}

	public Map<String, SearchFilter> buildFilter() {
		Map<String, Object> searchParams = new HashMap<String, Object>();
		Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
		return filters;
	}

	@Override
	public List<Entity> findByForm(final Form form) {
		QuerySpecification<Entity> spec = DynamicSpecifications.bySearchFilter(
				buildByForm(form).values(), getEntityClazz());
		return findAll(spec);
	}

	@Override
	public QueryPage<Entity> findAll(QuerySpecification<Entity> spec, Pageable pageable) {
		QueryPage<Entity> resPage = new QueryPage<Entity>();
		resPage.setQueryLine("&size=" + pageable.getPageSize()
				+ "&orders=id&direction=" + Direction.DESC);
		resPage.setPage(getRepository().findAll(
				spec,
				new PageRequest(pageable.getPageNumber(), pageable
						.getPageSize(), new Sort(Direction.DESC,
						new String[] { "id" }))));
		return resPage;
	}

	@Override
	public List<Entity> findAll(Specification<Entity> spec) {
		return getRepository().findAll(spec);
	}

	@Override
	public List<Entity> findAll() {
		return getRepository().findAll();
	
	}

	@Transactional
	@Override
	public void delete(Long id) {
		getRepository().delete(id);
	}

	@Transactional
	@Override
	public void delete(Entity obj) {
		getRepository().delete(obj);
	}
	
	@Transactional
	@Override
	public void delete(Iterable<Entity> entitys) {
		for (Entity entity : entitys) {
			getRepository().delete(entity);
		}
	}

}
